# -*- coding: utf-8 -*-
from openerp.osv import fields,osv
from openerp import tools
from openerp.tools.translate import _
from openerp.modules.registry import RegistryManager
from datetime import datetime
from dateutil.relativedelta import relativedelta
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT

CHARGE_TYPES = [
    ('normal','普通费用'),
    ('deposit','押金'),
    ('advances','预收款'),
    ('outbuildings','附属建筑费用')
]

CHARGE_MODES = [
    ('period','周期性'),
    ('temp','临时性')
]

UNIT_OF_PERIODS = [
    ('month','月'),
    ('day','日')
]

BILLING_TYPES = [
    ('building_area_price','建面计费'),
    ('inner_area_price','套内计费'),
    ('quota','默认计费'),
    ('meter_reading','抄表计费'),
    #('caculate_formula','计算公式')
]

class pm_cost_item(osv.osv):
	_name = 'pm.cost.item'

	_columns = {
		'name':fields.char(string='项目名称',size=100,required=True),
        'code':fields.char(string='项目编号',size=100,required=True),
        'billing_type':fields.selection(BILLING_TYPES,string='计费方式'),
        'price':fields.float(string='价格',required=True),
		'charge_type':fields.selection(CHARGE_TYPES,string='计费类型'),
        #'meter_category_id':fields.many2one('pm.meter.category',string='表计类别'),
		'charge_mode':fields.selection(CHARGE_MODES,string='收费方式'),
        'unit_of_measure':fields.many2one('pm.parameter',string='计量单位',domain="[('category_code','=','JLDW')]"),
        'unit_of_period':fields.selection(UNIT_OF_PERIODS,string='周期单位'),
        'charge_period':fields.integer(string='计费周期数'),
        'caculate_formula':fields.char(string='计费公式',size=200),
        'is_ladder_price':fields.boolean(string='是否阶梯价格'),
        'ladderprices':fields.one2many('pm.cost.item.ladderprice','cost_item_id',string='阶梯价格'),
        'remark':fields.text(string='备注'),
	}
pm_cost_item()

class pm_cost_item_ladderprice(osv.osv):
    _name = 'pm.cost.item.ladderprice'
    _columns = {
        'cost_item_id':fields.many2one('pm.cost.item',string='收费项',required=True,domain="[('is_ladder_price','=',True),('billing_type','=','meter_reading')]"),
        'order_num':fields.integer(string='阶梯级别',required=True),
        'min_value':fields.float(string='最小值',required=True),
        'max_value':fields.float(string='最大值',required=True),
        'price':fields.float(string='价格'),
        'remark':fields.text(string='备注')
    }
pm_cost_item_ladderprice()

class pm_cost_item_room(osv.osv):
    _name = 'pm.cost.item.room'

    def on_change_cost_item(self,cr,uid,ids,cost_item_id,room_id,context=None):
        values = {}
        if cost_item_id:
            cost_item = self.pool.get('pm.cost.item').read(cr,uid,cost_item_id,[],context=context)
            print cost_item
            if cost_item:
                values = {
                    'name':cost_item['name'],
                    'price':cost_item['price'],
                    'billing_type':cost_item['billing_type'],
                    'charge_type':cost_item['charge_type'],
                    #'meter_category_id':cost_item['meter_category_id'] and cost_item['meter_category_id'][0] or None,
                    'charge_mode':cost_item['charge_mode'],
                    'unit_of_measure':cost_item['unit_of_measure'],
                    'unit_of_period':cost_item['unit_of_period'],
                    'charge_period':cost_item['charge_period'],
                    'is_ladder_price':cost_item['is_ladder_price'],
                    'caculate_formula':cost_item['caculate_formula'],
                }
        #默认的开始收费时间为房间的入住时间
        if room_id:
            room = self.pool.get('pm.project.building.room').read(cr,uid,room_id,[],context=context)
            if room:
                values['billing_start_date'] = room['in_start_date']
                values['billing_end_date'] = room['in_end_date']
        return {'value':values}

    def check_cost_item_exists(self,cr,uid,ids,context=None):
        for item in self.read(cr,uid,ids,['room_id','cost_item_id'],context=context):
            print 'cost_item is %s' % item
            res_ids = self.search(cr,uid,[('id','!=',item['id']),('room_id','=',item['room_id'][0]),('cost_item_id','=',item['cost_item_id'][0])],context=context)
            print 're_ids is %s' % res_ids
            if res_ids:
                return False
        return True

    _columns = {
        'name':fields.char(string='项目名称',size=100,required=True),
        'project_id':fields.many2one('pm.project',string='所属管理处'),
        'area_id':fields.many2one('pm.project.area',string='所属区域',domain="[('project_id','=',project_id)]"),
        'building_id':fields.many2one('pm.project.building',string='所属楼栋',help='Building',domain="[('project_area_id','=',area_id)]"),
        'room_id':fields.many2one('pm.project.building.room',string='所属房间',required=True,domain="[('building_id','=',building_id)]"),
        'cost_item_id':fields.many2one('pm.cost.item',string='收费项目',required=True,domain="[('charge_mode','=','period'),('charge_type','=','normal')]"),
        'billing_type':fields.related('cost_item_id','billing_type',type='selection',selection=BILLING_TYPES,string='计费方式',readonly=True),
		'charge_type':fields.related('cost_item_id','charge_type',type='selection',selection=CHARGE_TYPES,string='计费类型',readonly=True),
        #'meter_category_id':fields.related('cost_item_id','meter_category_id',type='many2one',relation='pm.meter.category',string='表计类别',readonly=True),
        'charge_mode':fields.related('cost_item_id','charge_mode',type='selection',selection=CHARGE_MODES,string='收费方式',readonly=True),
        'unit_of_measure':fields.related('cost_item_id','unit_of_measure',type='many2one',relation='pm.parameter',string='计量单位',readonly=True),
        'unit_of_period':fields.related('cost_item_id','unit_of_period',type='selection',selection=UNIT_OF_PERIODS,string='周期单位',readonly=True),
        'is_ladder_price':fields.related('cost_item_id','is_ladder_price',type='boolean',string='是否阶梯价格',readonly=True),
        'price':fields.float(string='价格',required=True),
        'charge_period':fields.integer(string='计费周期数'),
        'caculate_formula':fields.char(string='计费公式',size=200),
        'is_charge':fields.boolean(string='是否收费'),
        'charge_period':fields.integer(string='计费周期数'),
        'print_order':fields.integer(string='打印次序'),
        'billing_start_date':fields.date(string='收费起始日期',required=True),
        'billing_end_date':fields.date(string='收费结束日期'),
        'add_time':fields.datetime(string='添加时间'),
        'remark':fields.text(string='Remark'),
    }

    _constraints = [
        (check_cost_item_exists,'此房间已经存在同样的收费项目！',['room_id','cost_item_id']),
    ]

    _defaults = {
        'is_charge':True,
    }
pm_cost_item_room()


class pm_cost_receivable(osv.osv):
    _name = 'pm.cost.receivable'

    def bt_check(self,cr,uid,ids,context=None):
        for item in self.browse(cr,uid,ids,context=context):
            self.write(cr,uid,ids,{'check_man':uid,'check_time':datetime.now()},context=context)
            #TODO add shoud received item in cost 
        return True

    def bt_uncheck(self,cr,uid,ids,context=None):
        for item in self.browse(cr,uid,ids,context=context):
            self.write(cr,uid,ids,{'check_man':uid,'check_time':datetime.now()},context=context)
            #TODO add shoud received item in cost 
        return True

    def btn_draft(self,cr,uid,ids,context=None):
        return True

    def calculate_amount(self,room_item,room_cost_item,start_date,end_date,actual_end_date):
        price = room_cost_item.price
        area = 0.0
        if room_cost_item.charge_mode=='temp':
            return price
        amount = 0.0
        d2 = end_date - start_date
        period_days = (end_date - start_date).days

        if end_date==actual_end_date:
            actual_days = period_days
        else:
            actual_days = (actual_end_date - start_date).days
        
        if room_cost_item.charge_mode=='period':
            if room_cost_item.billing_type=='building_area_price':
                amount = room_item.building_area * price * actual_days / period_days
            elif room_cost_item.billing_type =='inner_area_price':
                amount = room_item.inner_area * price * actual_days / period_days
            elif room_cost_item.billing_type == 'normal':
                amount = price * actual_days/period_days
        return amount,actual_days * 1.0/period_days

    def create(self,cr,uid,data,context=None):
        print 'create receiveable data is %s'%data
        context = context or {}
        ismeter = context.get('meter',None)
        if ismeter:
            return super(pm_cost_receivable,self).create(cr,uid,data,context=context)

        out = context.get('out',None)
        pcm_rep = self.pool.get('pm.cost.item.room')
        room_cost_item_ids = pcm_rep.search(cr,uid,[('room_id','=',data['room_id']), ('cost_item_id','=',data['cost_item_id'])],context=context)
        print 'room_cost_item_ids is %s,room_id is %s, cost_item_id is %s' % (room_cost_item_ids,data['room_id'],data['cost_item_id'])
        rct = pcm_rep.browse(cr,uid,room_cost_item_ids,context=context)[0]
        if out:
            if rct.billing_end_date and (datetime.strptime(rct.billing_end_date,DATE_FORMAT) < datetime.strptime(data['billing_start_date'],DATE_FORMAT)):
                print 'cannot genergate receiveable'
                return 0 
        else:
            if rct.billing_end_date and (datetime.strptime(rct.billing_end_date,DATE_FORMAT) < datetime.strptime(data['billing_start_date'],DATE_FORMAT)):
                print 'cannot genergate receiveable'
                raise osv.except_osv(_('不能生成应收'),_('已经超出收费日期'))

        room_id = data['room_id']
        partner_id = data['partner_id']
        cost_item_id = data['cost_item_id']
        actual_end_date = data['billing_end_date']
        values = self.calculate(cr,uid,[],room_id,partner_id,cost_item_id,actual_end_date,context=context)
        data['amount'] = values['amount']
        data['price'] = values['price']
        data['rate'] = values['rate']
        return super(pm_cost_receivable,self).create(cr,uid,data,context=context)

    def create_for_out(self,cr,uid,ids,room_id,partner_id,cost_item_id,actual_end_date,context=None):
        """
        供其他对象调用生成应收项，主要用在客户迁出时生成应收，此时如果时间超出范围不应抛出异常
        """
        context = context or {}
        context['out'] = True
        pcm_rep = self.pool.get('pm.cost.item.room')
        room_item = self.pool.get('pm.project.building.room').read(cr,uid,room_id,[],context=context)

        #没有收费对象的情况下不需要考虑费用生成
        if not room_item['hourseholder'][0]:
            return 0

        room_cost_item_ids = pcm_rep.search(cr,uid,[('room_id','=',room_id), ('cost_item_id','=',cost_item_id)],context=context)
        print 'room_cost_item_ids is %s,actual_end_date is %s '% (room_cost_item_ids,actual_end_date)
        rct = pcm_rep.browse(cr,uid,room_cost_item_ids,context=context)[0]
        if rct.billing_end_date and (datetime.strptime(rct.billing_end_date,DATE_FORMAT) < datetime.strptime(actual_end_date,DATE_FORMAT)):
            print 'cannot genergate receiveable'
            return 0 

        data = {}
        data['project_id'] = room_item['project_id'][0]
        data['area_id'] = room_item['area_id'][0]
        data['building_id'] = room_item['building_id'][0]
        data['room_id'] = room_id
        data['cost_item_id'] = cost_item_id
        values = self.calculate(cr,uid,[],room_id,partner_id,cost_item_id,actual_end_date,context=context)
        data['partner_id'] = values['partner_id']
        data['billing_start_date'] = values['billing_start_date']
        data['billing_end_date'] = values['billing_end_date']
        data['rate'] = values['rate']
        data['price'] = values['price']
        data['amount'] = values['amount']
        return self.create(cr,uid,data,context=context)

    def calculate(self,cr,uid,ids,room_id,partner_id,cost_item_id,actual_end_date,context=None):
        """
        计算当前房间的某个用户应收款项,返回结果如下：
        {'billing_start_date':'','billing_end_date':'','amount':0.0','price':0.0,'rate':0.0}
        """
        values = {}

        pcm_rep = self.pool.get('pm.cost.item.room')
        room_cost_item_ids = pcm_rep.search(cr,uid,[('room_id','=',room_id), ('cost_item_id','=',cost_item_id)],context=context)
        rct = pcm_rep.browse(cr,uid,room_cost_item_ids,context=context)[0]

        room_item = self.pool.get('pm.project.building.room').browse(cr,uid,room_id,context=context)
        if not partner_id:
            partner_id = room_item.hourseholder.id
        values['partner_id'] = partner_id
        
        #根据过往应收项计算出本次的开始资费日期
        rec_ids = self.search(cr,uid,[('cost_item_id','=',cost_item_id),('partner_id','=',partner_id)
                                    ,('room_id','=',room_id)],order='input_time desc',limit=1,context=context)
        start_date = None
        end_date = None
        if rec_ids:
            rec_item = self.read(cr,uid,rec_ids,[],context=context)[0]
            print 'rec_item is %s' % rec_item
            start_date = datetime.strptime(rec_item['billing_end_date'],DATE_FORMAT) + relativedelta(days=+1)
        
        #根据房间的资费项参数获取项目的资费周期及价格        
        unit_of_period = rct.unit_of_period
        charge_period = rct.charge_period

        """
        如果没有当前没有开始日期，则应该是收费项中设置的收费开始日期
        """
        if not start_date:
            start_date = datetime.strptime(rct.billing_start_date,DATE_FORMAT)
        """
        根据收费周期计算出理论上的收费结束日期
        """
        if unit_of_period == 'month':
            end_date = start_date + relativedelta(months=+charge_period) 
        elif unit_of_period =='day':
            end_date = start_date + relativedelta(days=+charge_period)

        if not actual_end_date:
            actual_end_date = end_date
        else:
            actual_end_date = datetime.strptime(actual_end_date,DATE_FORMAT)
        print 'actual_end_date is %s, billing_end_date is %s' %(actual_end_date,rct.billing_end_date)

        #如果收费的结束时间比收费项目中设置的结束时间大，则应以结束时间为准
        if actual_end_date > datetime.strptime(rct.billing_end_date,DATE_FORMAT):
            actual_end_date = datetime.strptime(rct.billing_end_date,DATE_FORMAT)

        values['amount'] ,values['rate'] = self.calculate_amount(room_item,rct,start_date,end_date,actual_end_date) 
        values['price'] = rct.price
        values['billing_start_date'] = datetime.strftime(start_date,DATE_FORMAT)
        values['billing_end_date'] = datetime.strftime(actual_end_date,DATE_FORMAT)
        return values

    def change_cost_item(self,cr,uid,ids,room_id,partner_id,cost_item_id,actual_end_date,context=None):
        values = self.calculate(cr,uid,ids,room_id,partner_id,cost_item_id,actual_end_date,context=context)
        return {'value':values}

    def change_billing_date(self,cr,uid,ids,room_id,partner_id,cost_item_id,billing_start_date,
                            billing_end_date, context=None):
        values = self.calculate(cr,uid,ids,room_id,partner_id,cost_item_id,billing_end_date,context=context)
        return {'value':values}

    _columns = {
        'name':fields.char(string='应收流水号',size=100),
        'project_id':fields.many2one('pm.project',string='管理处'),
        'area_id':fields.many2one('pm.project.area',string='区域',domain="[('project_id','=',project_id)]"),
        'building_id':fields.many2one('pm.project.building',string='楼栋',help='Building',domain="[('project_area_id','=',area_id)]"),
        'room_id':fields.many2one('pm.project.building.room',string='房间',required=True,help='房间',domain="[('building_id','=',building_id)]"),
        'partner_id':fields.many2one('res.partner',string='客户'),
        'cost_item_id':fields.many2one('pm.cost.item',domain="[('charge_type','=','normal')]",string='收费项目'),
        'start_value':fields.float(string='起数'),
        'end_value':fields.float(string='止数'),
        'count':fields.float(string='数量'),
        'price':fields.float(string='单价'),
        'rate':fields.float(string='倍率'),
        'amount':fields.float(string='金额'),
        'billing_start_date':fields.date(string='起始日期'),
        'billing_end_date':fields.date(string='截止日期'),
        'cost_month':fields.char(string='计费月份',size=6),
        'input_man':fields.many2one('res.users',string='录入人'),
        'input_time':fields.datetime(string='录入时间'),
        'check_man':fields.many2one('res.users',string='审核人'),
        'check_time':fields.datetime(string='审核时间'),
        'remark':fields.text(string='备注'),
        'meter_record_id':fields.many2one('pm.meter.record',string='抄表记录'),
        'charge_id':fields.many2one('pm.charge',string='收费流水'),
        'state':fields.selection([('recorded','已录入'),('checked','已审核'),('received','已收费'),('draft','已作废')],string='状态'),
    }
    _defaults = {
        'state':'recorded',
        'input_man': lambda self,cr,uid,ids,context=None:uid,
        'input_time':lambda self,cr,uid,ids: datetime.strftime(datetime.now(),'%Y-%m-%d %H:%M:%S')
    }
pm_cost_receivable()

